package com.cnoja.jmsncn.command;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

import org.apache.log4j.Logger;

import com.cnoja.jmsncn.command.event.CommandEvent;
import com.cnoja.jmsncn.inf.ICommand;
import com.cnoja.jmsncn.inf.ICommandEventListener;
import com.cnoja.jmsncn.inf.ICommander;
import com.cnoja.jmsncn.inf.IMessenger;
import com.cnoja.jmsncn.kernel.DefaultMessengerImpl;
import com.cnoja.jmsncn.kernel.exceptions.CommandException;

public class SocketCommander implements ICommander {

	private List<ICommandEventListener> listeners = new CopyOnWriteArrayList<ICommandEventListener>();

	private static Logger logger = Logger.getLogger(SocketCommander.class);
	private Socket socket = null;

	private Map<Long, ICommand> commandMap = new ConcurrentHashMap<Long, ICommand>();
	public SocketReponseReceiver receiverRunnable = null;
	public Thread receiverThread = null;

	private IMessenger messenger = null;

	public synchronized void start() throws IOException {
		this.socket = new Socket();
		if (this.receiverRunnable != null) {
			this.receiverRunnable.setRun(false);
		}
		this.receiverRunnable = new SocketReponseReceiver(this.socket,
				this.commandMap, this);
		this.receiverThread = new Thread(receiverRunnable);
		DefaultMessengerImpl defaultMessengerImpl = (DefaultMessengerImpl) this.messenger;

		socket.connect(new InetSocketAddress(defaultMessengerImpl.getHost(),
				defaultMessengerImpl.getPort()));

		if (logger.isInfoEnabled()) {
			logger.info("Connect to host:" + defaultMessengerImpl.getHost()
					+ ":" + defaultMessengerImpl.getPort());
		}
		if (receiverThread.isInterrupted() || (!receiverThread.isAlive())) {
			receiverRunnable.setRun(true);
			receiverThread.start();
		}

	}

	public synchronized void stop() throws IOException {
		if ((!receiverThread.isInterrupted()) || (receiverThread.isAlive())) {
			receiverRunnable.setRun(false);
		}
		this.socket.close();
	}

	public void execute(ICommand command) throws CommandException {
		command.setCommander(this);
		try {
			String commandString = command.toString();
			this.commandMap.put(command.getTransactionId(), command);
			socket.getOutputStream().write(commandString.getBytes());
			socket.getOutputStream().flush();
			if (logger.isInfoEnabled()) {
				logger.info("Sending command:"
						+ commandString.replaceAll("\n", "").replaceAll("\r",
								""));
			}
		} catch (IOException e) {
			throw new CommandException(e);
		}

	}

	public void setMessenger(IMessenger messenger) {
		this.messenger = messenger;

	}

	public IMessenger getMessenger() {
		return this.messenger;
	}

	public void fireCommanderEvent(CommandEvent event) {
		if (this.listeners != null) {
			for (ICommandEventListener eventListener : this.listeners) {
				eventListener.onCommandEvent(event);
			}
		}

	}

	public void addCommandEventListener(ICommandEventListener listener) {
		this.listeners.add(listener);
	}

	public void removeCommandEventListener(ICommandEventListener listener) {
		this.listeners.remove(listener);

	}

}
